namespace SimpleWebServices
{
    using System;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Xml.Serialization;
    using SimpleWebServices.Web.Mvc;

    public sealed class ObjectExpectations<T> : ITestObjectStyle, ITestObjectSealed, ITestObjectConstruction, ITestObject
    {
        public ObjectExpectations()
        {
            this.Items = new Collection<ITestExpectation>();
        }

        bool ITestObject.Result
        {
            get
            {
                return 0 == this.Items.Where(x => !x.Check()).Count();
            }
        }

        private Collection<ITestExpectation> Items
        {
            get;
            set;
        }

        public ITestObject IsInterface()
        {
            this.Items.Add(new InterfaceTest<T>());
            return this;
        }

        public ITestObject IsValueType()
        {
            this.Items.Add(new ValueTypeTest<T>());
            return this;
        }

        public ITestObjectStyle DerivesFrom<TBase>()
        {
            this.Items.Add(new BaseClassTest<T>() { Is = typeof(TBase) });
            return this;
        }

        ITestObject ITestObjectStyle.IsAbstractBaseClass()
        {
            this.Items.Add(new AbstractBaseClassTest<T>());
            return this;
        }

        ITestObjectSealed ITestObjectStyle.IsConcreteClass()
        {
            this.Items.Add(new ConcreteClassTest<T>());
            return this;
        }

        ITestObjectConstruction ITestObjectSealed.IsSealed()
        {
            this.Items.Add(new SealedClassTest<T>() { Value = true });
            return this;
        }

        ITestObjectConstruction ITestObjectSealed.IsUnsealed()
        {
            this.Items.Add(new SealedClassTest<T>() { Value = false });
            return this;
        }

        ITestObject ITestObjectConstruction.HasDefaultConstructor()
        {
            this.Items.Add(new DefaultConstructorTest<T>());
            return this;
        }

        ITestObject ITestObjectConstruction.NoDefaultConstructor()
        {
            return this;
        }

        ITestObject ITestObject.Implements<TInterface>()
        {
            if (!typeof(TInterface).IsInterface)
            {
                throw new TestException("An interface type must be specified.");
            }

            this.Items.Add(new ImplementationTest<T>() { Interface = typeof(TInterface) });
            return this;
        }

        ITestObject ITestObject.IsDecoratedWith<TAttribute>()
        {
            if (!typeof(TAttribute).IsSubclassOf(typeof(Attribute)))
            {
                throw new TestException("An attribute type must be specified.");
            }
            else if (typeof(AllowAttribute).IsAssignableFrom(typeof(TAttribute)))
            {
                throw new TestException("Please use the Allow(...) method instead of this generic method.");
            }
            else if (typeof(SerializableAttribute).IsAssignableFrom(typeof(TAttribute)))
            {
                throw new TestException("Please use the Serializable() method instead of this generic method.");
            }
            else if (typeof(XmlRootAttribute).IsAssignableFrom(typeof(TAttribute)))
            {
                throw new TestException("Please use the XmlRoot(...) method instead of this generic method.");
            }

            this.Items.Add(new DecorationTest(typeof(T)) { Attribute = typeof(TAttribute) });
            return this;
        }

        ITestObject ITestObject.Allow(string methods)
        {
            this.Items.Add(new AllowDecorationTest<T>() { Methods = methods });
            return this;
        }

        ITestObject ITestObject.Resource(string path)
        {
            this.Items.Add(new ResourceDecorationTest<T>() { Path = path });
            return this;
        }

        ITestObject ITestObject.IsInRole(string roles, string schemes)
        {
            this.Items.Add(new IsInRoleDecorationTest<T>() { Roles = roles, Schemes = schemes });
            return this;
        }

        ITestObject ITestObject.Serializable()
        {
            this.Items.Add(new DecorationTest(typeof(T)) { Attribute = typeof(SerializableAttribute) });
            this.Items.Add(new ImplementationTest<T>() { Interface = typeof(ISerializable) });
            return this;
        }

        ITestObject ITestObject.XmlRoot(string name)
        {
            this.Items.Add(new XmlRootDecorationTest<T>() { Name = name });
            return this;
        }

        ITestObject ITestObject.XmlRoot(string name, string @namespace)
        {
            this.Items.Add(new XmlRootDecorationTest<T>() { Name = name, Namespace = @namespace });
            return this;
        }

        ITestObject ITestObject.IsNotDecorated()
        {
            this.Items.Add(new DecorationTest(typeof(T)) { Attribute = null });
            return this;
        }
    }
}